package com.tuanzi.loan.business.service.product.product.impl;

import java.lang.reflect.InvocationTargetException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

import com.alibaba.fastjson.JSON;
import com.sun.faces.util.CollectionsUtils;
import com.tuanzi.loan.business.constants.product.LoanProductDeviceType;
import com.tuanzi.loan.business.service.product.product.LoanProductConsumeRecordService;
import com.tuanzi.loan.business.service.statistics.LoanProductClickUvService;
import com.tuanzi.loan.core.util.GsonUtils;
import com.tuanzi.loan.vo.LoanProductOnlineRule;
import com.tuanzi.loan.vo.view.family.product.ProductInfoView;
import com.tuanzi.loan.vo.view.product.LoanProductInfoView;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.TaskExecutor;
import org.springframework.data.domain.Page;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.retry.RetryException;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.gson.Gson;
import com.tuanzi.loan.business.entity.common.CommProductChannel;
import com.tuanzi.loan.business.entity.common.CommProductId;
import com.tuanzi.loan.business.entity.family.ContentActivity;
import com.tuanzi.loan.business.entity.family.ContentActivityVars;
import com.tuanzi.loan.business.entity.family.ProductInfoTag;
import com.tuanzi.loan.business.entity.family.ProductInfoTagType;
import com.tuanzi.loan.business.entity.product.product.*;
import com.tuanzi.loan.business.enums.ModuleContentItemType;
import com.tuanzi.loan.business.repository.product.product.*;
import com.tuanzi.loan.business.service.common.CommProductChannelService;
import com.tuanzi.loan.business.service.common.CommProductIdService;
import com.tuanzi.loan.business.service.family.ContentActivityService;
import com.tuanzi.loan.business.service.family.ContentActivityVarsService;
import com.tuanzi.loan.business.service.family.ProductInfoTagService;
import com.tuanzi.loan.business.service.product.product.LoanProductInfoService;
import com.tuanzi.loan.core.aspect.Debug;
import com.tuanzi.loan.core.repository.JPAAuditRepository;
import com.tuanzi.loan.core.service.BaseAuditServiceImpl;
import com.tuanzi.loan.core.vo.PageSearcher;
import com.tuanzi.loan.vo.request.product.product.LoanProductInfoSearchRequest;
import com.tuanzi.loan.vo.response.product.product.LoanProductInfoSearchResponse;
import com.tuanzi.loan.vo.view.product.LoanProductInfoView;
import com.tuanzi.loan.vo.view.product.LoanProductJoinedLimit;
import com.tuanzi.loan.vo.view.product.LoanProductModelView;
import com.tuanzi.loan.vo.view.product.LoanProductTopic;
import com.tuanzi.server.loan_base.constant.LoanRedisKey;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.TaskExecutor;
import org.springframework.data.domain.Page;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.retry.RetryException;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Ray 2017/09/21
 */
@Slf4j
@Debug
@Service
@Transactional
@Retryable(value = { RetryException.class })
public class LoanProductInfoServiceImpl extends BaseAuditServiceImpl<LoanProductInfo, Long> implements LoanProductInfoService {

	private static final Set<String> PLATFORMS = Sets.newHashSet("android", "ios");
	private static final String PRODUCT_PREPAY_KEY = "loan_product_prepay_balance:";
	public static final String PRODUCT_HAD_OPEN_PERPAY_SWITCH_KEY = "loan_product_had_open_prepay_switch:";

	@Autowired
	private StringRedisTemplate stringRedisTemplate;
	@Autowired
	private LoanProductInfoRepository loanProductInfoRepository;
	@Autowired
	private ProductInfoTagService productInfoTagService;
	@Autowired
	private ContentActivityService contentActivityService;
	@Autowired
	private ContentActivityVarsService contentActivityVarsService;
	@Autowired
	private TaskExecutor taskExecutor;
	@Autowired
	private LoanProductOnlineProductRepository loanProductOnlineProductRepository;
	@Autowired
	private LoanProductPlatformRepository loanProductPlatformRepository;
	@Autowired
	private LoanProductExcludeChannelRepository loanProductExcludeChannelRepository;
	@Autowired
	private CommProductIdService commProductIdService;
	@Autowired
	private CommProductChannelService commProductChannelService;
	@Autowired
	private LoanProductBusinessInfoRepository loanProductBusinessInfoRepository;
	@Autowired
	private LoanProductRechargeRecordRepository rechargeRecordRepository;
	@Autowired
	private LoanProductApiFicoTagRepository loanProductApiFicoTagRepository;
	@Autowired
	private LoanProductOnlineInfoRepository loanProductOnlineInfoRepository;

	@Autowired
	private LoanProductConsumeRecordService consumeRecordService;
	@Autowired
	private LoanProductClickUvService clickUvService;

	@Override
	protected JPAAuditRepository<LoanProductInfo, Long> getRepository() {
		return loanProductInfoRepository;
	}

	@Override
	public LoanProductInfoView loadProductInfoView(Long productInfoId) {
		LoanProductInfoView request = new LoanProductInfoView();
		LoanProductInfo productInfo = loanProductInfoRepository.findOne(productInfoId);

		DateTime now = DateTime.now();
		if (productInfo.getEveryDayOnlineTime() != null) {
			request.setEveryDayOnlineTime(new DateTime(now.getYear(), now.getMonthOfYear(), now.getDayOfMonth(), 0, 0, 0).plusSeconds(productInfo.getEveryDayOnlineTime()).toDate());
		}
		if (productInfo.getEveryDayOfflineTime() != null) {
			request.setEveryDayOfflineTime(new DateTime(now.getYear(), now.getMonthOfYear(), now.getDayOfMonth(), 0, 0, 0).plusSeconds(productInfo.getEveryDayOfflineTime()).toDate());
		}

		BeanUtils.copyProperties(productInfo, request);

		request.setProductJoinedLimit(
				StringUtils.isBlank(productInfo.getJoinedLimit()) ? new LoanProductJoinedLimit() : new Gson().fromJson(productInfo.getJoinedLimit(), LoanProductJoinedLimit.class));
		request.setLoanProductTopic(
				StringUtils.isBlank(productInfo.getTopicInfo()) ? new LoanProductTopic() : new Gson().fromJson(productInfo.getTopicInfo(), LoanProductTopic.class));
		request.getLoanProductTopic()
				.setTopicInfos(CollectionUtils.isEmpty(request.getLoanProductTopic().getTopicInfos()) ? Lists.newArrayList() : request.getLoanProductTopic().getTopicInfos());
		request.setSelectedExcludeChannels(loanProductExcludeChannelRepository.findAllByProductId(productInfoId).stream().map(LoanProductExcludeChannel::getChannelId)
				.map(x -> x.toString()).collect(Collectors.toSet()));
		request.setSelectedPlatforms(loanProductPlatformRepository.findAllByProductId(productInfoId).stream().map(LoanProductPlatform::getPlatform).map(ProductPlatformType::name)
				.collect(Collectors.toList()));
		request.setSelectedOnlineProducts(loanProductOnlineProductRepository.findAllByProductId(productInfoId).stream().map(LoanProductOnlineProduct::getCommProductId)
				.map(x -> x.toString()).collect(Collectors.toSet()));

		LoanProductApiFicoTag loanProductApiFicoTag = loanProductApiFicoTagRepository.findOneByProductId(productInfoId);
		if(loanProductApiFicoTag != null && StringUtils.isNotBlank(loanProductApiFicoTag.getTags())){
			request.setSelectedApiFicoTags(Lists.newArrayList(StringUtils.split(loanProductApiFicoTag.getTags(), ",")));
		}

		LoanProductOnlineInfo loanProductOnlineInfo = loanProductOnlineInfoRepository.findOneByProductId(productInfoId);
		if(loanProductOnlineInfo != null){
			LoanProductOnlineRule loanProductOnlineRule = new Gson().fromJson(loanProductOnlineInfo.getRule(), LoanProductOnlineRule.class);
			request.setOnlineRule(loanProductOnlineRule);
        }

        //设备限制默认勾选新老用户
        if(request.getOnlineRule() == null || CollectionUtils.isEmpty(request.getOnlineRule().getDevice())){
			LoanProductOnlineRule onlineRule = new LoanProductOnlineRule();
			onlineRule.setDevice(Lists.newArrayList(LoanProductDeviceType.OLD_USER.getType(), LoanProductDeviceType.NEW_USER.getType()));
			request.setOnlineRule(onlineRule);
		}

		request.setProductInfoId(productInfo.getId());

		LoanProductBusinessInfo loanProductBusinessInfo =  loanProductBusinessInfoRepository.findOneByProductId(productInfo.getId());
		if(loanProductBusinessInfo != null && loanProductBusinessInfo.getUvVAlue() != null){
			request.setUvValue(loanProductBusinessInfo.getUvVAlue());
		}

		if (StringUtils.isNotBlank(productInfo.getAdvantage())) {
			request.setAdvantage(Arrays.asList(StringUtils.split(productInfo.getAdvantage(), ",")));
		}
		getProductModelView(request);
		return request;
	}

	private void getProductModelView(LoanProductInfoView request) {
		ContentActivity contentActivity = contentActivityService.findOne(request.getModelId());
		if (null != contentActivity) {
			request.setActivityModelId(contentActivity.getMid());
			request.setActivityName(contentActivity.getName());
			request.setActivityMemo(contentActivity.getMemo());
			request.setActivityUrl(contentActivity.getTargetUrl());
			List<LoanProductModelView> loanProductModelViews = contentActivityService.loadActivityModelView(contentActivity.getMid(), contentActivity.getId());
			request.setLoanProductModelViews(loanProductModelViews);
		}
	}

	@Override
	public LoanProductInfoView saveOrUpdateProductInfoView(LoanProductInfoView view) {
		LoanProductInfo productInfo;

		int oldType = -1;
		if (view.getProductInfoId() != null) {
			productInfo = loanProductInfoRepository.findOne(view.getProductInfoId());
		} else {
			productInfo = new LoanProductInfo();
		}

		BeanUtils.copyProperties(view, productInfo);
		if (view.getEveryDayOnlineTime() != null) {
			productInfo.setEveryDayOnlineTime(new DateTime(view.getEveryDayOnlineTime()).getSecondOfDay());
		}else{
			productInfo.setEveryDayOnlineTime(null);
		}
		if (view.getEveryDayOfflineTime() != null) {
			productInfo.setEveryDayOfflineTime(new DateTime(view.getEveryDayOfflineTime()).getSecondOfDay());
		}else{
			productInfo.setEveryDayOfflineTime(null);
		}

		if (productInfo.getApiModelId() == null) {
			productInfo.setApiModelId("0");
		}
		if (productInfo.getStatus() == null) {
			productInfo.setStatus(0);
		}

		if (productInfo.getApi() == null) {
			productInfo.setApi(0);
		}

        Long modelId = saveProductActivityModel(view);
        productInfo.setModelId(modelId);
        view.setModelId(modelId);

		// 产品 advantages 可为空
		if (CollectionUtils.isEmpty(view.getAdvantage())) {
			view.setAdvantage(Lists.newArrayList());
		}
		productInfo.setAdvantage(view.getAdvantage().stream().collect(Collectors.joining(",")));
		productInfo.setTopicInfo(CollectionUtils.isEmpty(view.getLoanProductTopic().getTopicInfos()) ? null : new Gson().toJson(view.getLoanProductTopic()));


		if (productInfo.getId() != null) {
			productInfoTagService.delete(productInfoTagService.findAllByProductInfoId(productInfo.getId()));
			loanProductOnlineProductRepository.deleteByProductId(productInfo.getId());
			loanProductPlatformRepository.deleteByProductId(productInfo.getId());
			loanProductExcludeChannelRepository.deleteByProductId(productInfo.getId());
		}

		if (view.getProductJoinedLimit() != null){
			productInfo.setJoinedLimit(new Gson().toJson(view.getProductJoinedLimit()));
		}

		saveOrUpdate(productInfo);

		loanProductOnlineProductRepository.save(view.getSelectedOnlineProducts().stream().map(x -> NumberUtils.toLong(x)).map(x -> {
			LoanProductOnlineProduct productOnlineProduct = new LoanProductOnlineProduct();
			productOnlineProduct.setCommProductId(x);
			productOnlineProduct.setProductId(productInfo.getId());
			return productOnlineProduct;
		}).collect(Collectors.toList()));

		loanProductPlatformRepository.save(view.getSelectedPlatforms().stream().map(x -> {
			LoanProductPlatform productPlatform = new LoanProductPlatform();
			productPlatform.setPlatform(ProductPlatformType.valueOf(x));
			productPlatform.setProductId(productInfo.getId());
			return productPlatform;
		}).collect(Collectors.toList()));

		loanProductExcludeChannelRepository.save(view.getSelectedExcludeChannels().stream().map(x -> NumberUtils.toLong(x)).map(x -> {
			LoanProductExcludeChannel productExcludeChannel = new LoanProductExcludeChannel();
			productExcludeChannel.setChannelId(x);
			productExcludeChannel.setProductId(productInfo.getId());
			return productExcludeChannel;
		}).collect(Collectors.toList()));

		//用户标签
		LoanProductApiFicoTag loanProductApiFicoTag = loanProductApiFicoTagRepository.findOneByProductId(productInfo.getId());
		if(loanProductApiFicoTag == null){
			loanProductApiFicoTag = new LoanProductApiFicoTag();
			loanProductApiFicoTag.setProductId(productInfo.getId());
		}
		loanProductApiFicoTag.setTags(StringUtils.join(view.getSelectedApiFicoTags(), ","));
		loanProductApiFicoTagRepository.save(loanProductApiFicoTag);

		//上线配置）
		LoanProductOnlineInfo loanProductOnlineInfo = loanProductOnlineInfoRepository.findOneByProductId(productInfo.getId());
		if(loanProductOnlineInfo == null){
			loanProductOnlineInfo = new LoanProductOnlineInfo();
			loanProductOnlineInfo.setProductId(productInfo.getId());
		}

		loanProductOnlineInfo.setRule(new Gson().toJson(view.getOnlineRule()));
		loanProductOnlineInfoRepository.save(loanProductOnlineInfo);


		if (CollectionUtils.isNotEmpty(view.getNewRechargeRecordList())) {
			rechargeRecordRepository.save(view.getNewRechargeRecordList().stream().map(x -> {
				LoanProductRechargeRecord rechargeRecord = new LoanProductRechargeRecord();
				try {
					org.apache.commons.beanutils.BeanUtils.copyProperties(rechargeRecord, x);
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					e.printStackTrace();
				}
				rechargeRecord.setProductId(productInfo.getId());
				return rechargeRecord;
			}).collect(Collectors.toList()));
		}
		stringRedisTemplate.delete(PRODUCT_PREPAY_KEY + productInfo.getId()); //清除预付余额缓存
		LoanProductBusinessInfo loanProductBusinessInfo =  loanProductBusinessInfoRepository.findOneByProductId(productInfo.getId());
		if(loanProductBusinessInfo == null){
			loanProductBusinessInfo = new LoanProductBusinessInfo();
			loanProductBusinessInfo.setProductId(productInfo.getId());
		}
		loanProductBusinessInfo.setUvVAlue(view.getUvValue());
		loanProductBusinessInfoRepository.save(loanProductBusinessInfo);

		view.setProductInfoId(productInfo.getId());
		if (productInfo.getEveryDayOnlineTime() != null) {
			stringRedisTemplate.boundSetOps("auto-online-products-daily:" + DateTime.now().toString(DateTimeFormat.forPattern("yyyyMMdd"))).add(productInfo.getId().toString());
		} else {
			stringRedisTemplate.boundSetOps("auto-online-products-daily:" + DateTime.now().toString(DateTimeFormat.forPattern("yyyyMMdd"))).remove(productInfo.getId().toString());
			stringRedisTemplate.boundSetOps("auto-online-products-daily:" + DateTime.now().plusDays(-1).toString(DateTimeFormat.forPattern("yyyyMMdd")))
					.remove(productInfo.getId().toString());
		}
		cleanProductRedis(productInfo.getId());
		cleanSpaceRedis();

		return view;
	}

	private Long saveProductActivityModel(LoanProductInfoView view) {
		ContentActivity contentActivity;
		if (view.getProductInfoId() != null) {
            contentActivity = contentActivityService.findOne(view.getModelId());
			if (!contentActivity.getMid().equals(view.getActivityModelId())) {
				contentActivity.setMid(view.getActivityModelId());
			}
		} else {
			contentActivity = new ContentActivity();
			contentActivity.setMid(view.getActivityModelId());
			contentActivity.setCtime(new Date());
		}
		contentActivity.setMemo(view.getActivityMemo());
		contentActivity.setName(view.getActivityName());
		contentActivity.setTargetUrl(view.getActivityUrl());
		contentActivity = contentActivityService.saveOrUpdate(contentActivity);
		saveModelItem(contentActivity.getId(), view.getLoanProductModelViews());
		return contentActivity.getId();
	}

	private void saveModelItem(Long id, List<LoanProductModelView> productModelViewList) {
	    contentActivityVarsService.deleteAllByAid(id);
		if (null != productModelViewList && !productModelViewList.isEmpty()) {
			Date createTime = new Date();
			productModelViewList.stream().forEach(view -> {
				String value = getValue(view);
				if (StringUtils.isBlank(value)) {
					return;
				}

				ContentActivityVars vars = new ContentActivityVars();
				vars.setAid(id);
				vars.setContent(view.getContent());
				vars.setVal(value);
				vars.setCreateTime(createTime);
				contentActivityVarsService.saveOrUpdate(vars);
			});
		}
	}

	public String getValue(LoanProductModelView view) {
		String value = "";
		ModuleContentItemType itemType = ModuleContentItemType.getEnum(view.getType());
		if (null != itemType) {
			switch (itemType) {
			case INPUT_TEXT:
				value = view.getText();
				break;
			case IMAGE:
				value = view.getImgUrl();
				break;
			case IMAGE_TEXT:
				if (StringUtils.isNotBlank(view.getImgText()) && StringUtils.isNotBlank(view.getImgTextUrl())) {
					value = view.getImgText() + LoanProductModelView.SEPARATOR + view.getImgTextUrl();
				}
				break;
			case DATE:
				if (null != view.getDate()) {
					DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
					value = format.format(view.getDate());
				}
				break;
			case RADIO_BUTTON:
				value = String.valueOf(view.getIsTrue());
				break;
			case LIST:
				List<LoanProductModelView.OneListObject> list = view.getOneListObjects();
				if (null != list && !list.isEmpty()) {
					value = list.stream().filter(i -> StringUtils.isNotBlank(i.getText())).map(LoanProductModelView.OneListObject::getText)
							.collect(Collectors.joining(LoanProductModelView.SEPARATOR));
				}
				break;
			default:
				throw new RuntimeException("没有匹配的类型:" + itemType);
			}
		}
		return value;
	}

	public void cleanProductRedis(Long productId) {
		taskExecutor.execute(() -> {
			stringRedisTemplate.delete(LoanRedisKey.LOAN_ACTIVITY_INFO + productId);
			stringRedisTemplate.delete(LoanRedisKey.LOAN_ONLINE_ACTIVITY_INFO + productId);
			stringRedisTemplate.delete(LoanRedisKey.LOAN_DETAIL_ACTIVITY_ID + productId);
			stringRedisTemplate.delete(LoanRedisKey.LOAN_PRODUCT_TAGS + productId);
			stringRedisTemplate.delete("loan-app:product:" + productId);
		});
	}

	public void cleanSpaceRedis() {
		List<Integer> commProductPrdids = commProductIdService.findAll().parallelStream().map(CommProductId::getProductId).collect(Collectors.toList());
		List<Integer> commProductChannels = commProductChannelService.findAll().parallelStream().map(CommProductChannel::getChannelCode).collect(Collectors.toList());
		taskExecutor.execute(() -> {

			commProductPrdids.stream().forEach(x -> {
				PLATFORMS.stream().forEach(y -> {
					commProductChannels.stream().forEach(z -> {
						stringRedisTemplate.delete("loan-app:products:" + x + ":" + y + ":" + z);
					});
				});
			});
		});
	}

	private List<ProductInfoTag> structureProductInfoTag(Long productInfoId, ProductInfoTagType tagType, List<String> tagValues) {
		return tagValues.parallelStream().map(x -> {
			ProductInfoTag productInfoTag = new ProductInfoTag();
			productInfoTag.setProductInfoId(productInfoId);
			productInfoTag.setTagType(tagType);
			productInfoTag.setTagValue(x);
			return productInfoTag;
		}).collect(Collectors.toList());
	}

	private List<String> mapFromProductInfoTag(List<ProductInfoTag> productInfoTags) {
		if (CollectionUtils.isEmpty(productInfoTags)) {
			return Lists.newArrayList();
		}
		return productInfoTags.parallelStream().map(ProductInfoTag::getTagValue).map(String::toString).collect(Collectors.toList());
	}

	@Override
	public Page<LoanProductInfoSearchResponse> list(PageSearcher<LoanProductInfoSearchRequest> pageSearcher) {
		return loanProductInfoRepository.list(pageSearcher.getSearchCondition(), pageSearcher.getPageable());
	}

	@Override
	public void online(LoanProductInfo productInfo) {
		online(Lists.newArrayList(productInfo));
	}

	@Override
	public void sortDelete(LoanProductInfo productInfo) {
		// 删除父产品缓存
		cleanProductRedis(productInfo.getId());
		// 开始删除
		if (productInfo.getId() != null) {
			productInfoTagService.delete(productInfoTagService.findAllByProductInfoId(productInfo.getId()));
			loanProductOnlineProductRepository.deleteByProductId(productInfo.getId());
			loanProductPlatformRepository.deleteByProductId(productInfo.getId());
			loanProductExcludeChannelRepository.deleteByProductId(productInfo.getId());
		}
		loanProductInfoRepository.deleteBy(productInfo.getId());
	}

	@Override
	public void online(List<LoanProductInfo> productInfos) {
		productInfos.stream().forEach(x -> {
			x.setStatus(1);
			x.setOnlineTime(new Date());
			x.setEveryDayOnlineTime(null);
			saveOrUpdate(x);
		});
	}

	@Override
	public void offline(List<LoanProductInfo> productInfos) {
		productInfos.stream().forEach(x -> {
			x.setStatus(0);
			x.setUpdateTime(new Date());
			x.setEveryDayOnlineTime(null);
			saveOrUpdate(x);
			// 删除父产品缓存
			cleanProductRedis(x.getId());
		});
	}

	@Override
	public void offline(LoanProductInfo productInfo) {
		offline(Lists.newArrayList(productInfo));
	}

	private static Date getTomorrowDate() {
		return java.sql.Date.valueOf(new Date().toInstant().atZone(ZoneId.systemDefault()).toLocalDate().plusDays(1));
	}

	/**
	 * 查询公司所有的产品
	 *
	 * @param companyId
	 * @return
	 */
	@Override
	public List<LoanProductInfo> listAllProductInfoByCompanyId(Long companyId) {
		return loanProductInfoRepository.listAllProductInfoByCompanyId(companyId);
	}

	@Override
	public Long sumDailyClickUvLimitationByOnline(LoanProductInfoSearchRequest searchRequest) {
		return loanProductInfoRepository.sumDailyClickUvLimitationByOnline(searchRequest);
	}

	@Override
	public Long sumDailyClickUvLimitation(LoanProductInfoSearchRequest searchRequest) {
		return loanProductInfoRepository.sumDailyClickUvLimitation(searchRequest);
	}

	@Override
	public Long sumDailyClickUvRemainingByOnline(LoanProductInfoSearchRequest searchRequest) {
		return loanProductInfoRepository.sumDailyClickUvRemainingByOnelin(searchRequest);
	}

	@Override
	public Long countOnline(LoanProductInfoSearchRequest searchRequest) {
		return loanProductInfoRepository.countOnline(searchRequest);
	}

	@Override
	public Long countAutoOnline(LoanProductInfoSearchRequest searchRequest) {
		return loanProductInfoRepository.countAutoOnline(searchRequest);
	}

	@Override
	public void cleanProductCache(LoanProductInfo productInfo) {
		List<LoanProductPlatform> platforms = loanProductPlatformRepository.findAllByProductId(productInfo.getId());
		List<LoanProductOnlineProduct> onlineProductList = loanProductOnlineProductRepository.findAllByProductId(productInfo.getId());
		List<LoanProductExcludeChannel> excludeChannelList = loanProductExcludeChannelRepository.findAllByProductId(productInfo.getId());
		List<CommProductChannel> allChannelCodes = commProductChannelService.findAll();
		List<CommProductChannel> includeChannel = null;
		if (excludeChannelList != null){
			List<Integer> collect = excludeChannelList.stream().map(x -> x.getChannelId().intValue()).collect(Collectors.toList());
			//通过剔除渠道筛选出包含的所有渠道
			includeChannel = allChannelCodes.stream().filter(c -> !collect.contains(c.getChannelCode())).collect(Collectors.toList());
		}else{
			includeChannel = new ArrayList<>();
		}
		List<String> keys = new ArrayList<>();
		for (LoanProductPlatform platform : platforms){
			for (LoanProductOnlineProduct onlineProduct : onlineProductList){
				for (CommProductChannel channel : includeChannel){
					keys.add("loan-product:" + "prdid:" + onlineProduct.getProductId()
							+ ":channel:" + channel.getChannelCode() + ":platform:" + platform.getPlatform());
				}
			}
		}
//		stringRedisTemplate.delete(keys);
		taskExecutor.execute(() -> {
			stringRedisTemplate.delete(keys);
		});
	}

	/**
	 * 记录昨天一天的消费金额(0点定时任务触发)
	 * @param proid
	 */
	@Override
	public void saveYesterdayConsumeMoney(Long proid){
		double balanceCache = takeBeforeTodayBalanceCache(proid);
		Integer clickUv = clickUvService.countTodayProductClickUvCustom(proid,LocalDate.now().minusDays(1)) == null ? 0 : clickUvService.countTodayProductClickUvCustom(proid,LocalDate.now().minusDays(1)).intValue();
		LoanProductBusinessInfo loanProductBusinessInfo =  loanProductBusinessInfoRepository.findOneByProductId(proid);
		if (loanProductBusinessInfo != null){
			double todayConsumeTotal =  clickUv * loanProductBusinessInfo.getUvVAlue();
			LoanProductConsumeRecord consumeRecord = new LoanProductConsumeRecord();
			consumeRecord.setConsume_money(todayConsumeTotal);
			LocalDate localDate = LocalDate.now().minusDays(1);
			ZoneId zone = ZoneId.systemDefault();
			Instant instant = localDate.atStartOfDay().atZone(zone).toInstant();
			consumeRecord.setCreate_date(Date.from(instant));
			consumeRecord.setProductId(proid);
			consumeRecord.setUv_num(clickUv.longValue());
			consumeRecord.setUv_value(loanProductBusinessInfo.getUvVAlue());
			consumeRecordService.saveOrUpdate(consumeRecord);

			//更新每日余额
			double balance = balanceCache - todayConsumeTotal ;
			putTodayBalanceCache(proid,balance);
		}
	}

	/**
	 * 0点计算一次余额（不是实时的）
	 * @param balance
	 */
	public void putTodayBalanceCache(Long proid, Double balance){
		String cacheKey = PRODUCT_PREPAY_KEY + proid;
		stringRedisTemplate.opsForValue().set(cacheKey, balance.toString());
	}

	/**
	 * 取出缓存中0点计算的余额
	 * @param proid
	 * @return
	 */
	@Override
	public double takeBeforeTodayBalanceCache(Long proid){
		String cacheKey = PRODUCT_PREPAY_KEY + proid;
		String b = stringRedisTemplate.opsForValue().get(cacheKey);
		if (!StringUtils.isEmpty(b)){
			return Double.parseDouble(b);
		}else{
			List<LoanProductRechargeRecord> rechargeRecord = rechargeRecordRepository.findAllByProductId(proid);
			double totalRecharge = rechargeRecord.stream().mapToDouble(LoanProductRechargeRecord::getAmount).sum();
			double beforeTotleConsume = consumeRecordService.countConsumeMoneyByProdid(proid) == null ? 0:consumeRecordService.countConsumeMoneyByProdid(proid);
			Double balance = totalRecharge - beforeTotleConsume;
			stringRedisTemplate.opsForValue().set(cacheKey, balance.toString());
			return balance;
		}
	}

	/**
	 * 查询甲方实时预付余额（总充值 - （今天消耗 + 今天之前消费信息表的总和））
	 * @param proid
	 * @param clickUv
	 * @return
	 */
	@Override
	public double getBalance(Long proid,Integer clickUv){
		double oldBalance = takeBeforeTodayBalanceCache(proid);
		String cacheHadOpenPrepay = PRODUCT_HAD_OPEN_PERPAY_SWITCH_KEY + LocalDate.now().toString();
		Set<String> openPrepaySwitchIds = stringRedisTemplate.opsForSet().members(cacheHadOpenPrepay);
		if (openPrepaySwitchIds.contains(proid.toString())){
			double todayConsume = clickUv * getProductUvValue(proid) ;//计算当天还没有记录的消费金额
			return oldBalance - todayConsume;
		}
		return oldBalance;
	}

	@Override
	public Integer getClickUv(Long proid,LocalDate date){
		return clickUvService.countTodayProductClickUvCustom(proid,date) == null ? 0 : clickUvService.countTodayProductClickUvCustom(proid,date).intValue();
	}

	@Override
	public Float getProductUvValue(Long proid) {
		LoanProductBusinessInfo loanProductBusinessInfo =  loanProductBusinessInfoRepository.findOneByProductId(proid);
		if (loanProductBusinessInfo != null){
			return loanProductBusinessInfo.getUvVAlue();
		}
		return 0f;
	}
}
